﻿using System;
using System.Collections.Generic;
using System.Data.Objects.DataClasses;
using System.Linq;

namespace NerdDinnerDataComponent
{
    public class DinnerRepository : IDinnerRepository
    {
        private readonly NerdDinnerDataEntities db = new NerdDinnerDataEntities();

     

        public IList<DinnerInfo> FindUpcomingDinners()
        {
            var qry = (from dinner in db.Dinners
                   where dinner.EventDate >= DateTime.Now
                   orderby dinner.EventDate
                   select dinner);
            var list =  Mapper.CreateFrom(qry);
            return list;
        }


        public PaginatedDinnerInfoResultList FindUpcomingDinners(int pageIndex, int pageSize)
        {
            IQueryable<Dinner> qry = (from dinner in db.Dinners
                       where dinner.EventDate >= DateTime.Now
                       orderby dinner.EventDate
                       select dinner);
            PaginatedList<Dinner> pagList = new PaginatedList<Dinner>(qry, pageIndex, pageSize);
            return new PaginatedDinnerInfoResultList()
            {
                Result = Mapper.CreateFrom(pagList),
                PageIndex = pageIndex,
                PageSize = pageSize,
                TotalCount = pagList.TotalCount,
                TotalPages = pagList.TotalPages,
                HasNextPage = pagList.HasNextPage,
                HasPreviousPage = pagList.HasPreviousPage
            };
        }



        //
        // Query Methods

        public IList<DinnerInfo> FindDinnersByText(string q)
        {
            var qry=  db.Dinners.Where(d => d.Title.Contains(q)
                                         || d.Description.Contains(q)
                                         || d.HostedBy.Contains(q));
            var list = Mapper.CreateFrom(qry);
            return list;
        }

        public IList<DinnerInfo> FindAllDinners()
        {
            var qry=  db.Dinners;
            var list = Mapper.CreateFrom(qry);
            return list;
        
        }


        public DinnerInfo GetDinner(int id)
        {
            var entity =  db.Dinners.SingleOrDefault(d => d.DinnerID == id);
            if (entity!=null) 
                return Mapper.CreateFrom((entity));
            else
            {
                return null;
            }
           
        }

        //
        // Insert/Delete Methods

        public void Add(DinnerInfo dinner)
        {
            Dinner entity = Mapper.CreateFrom(dinner);

            db.Dinners.AddObject(entity);
        }

        public void Delete(DinnerInfo dinner)
        {
            var entity = db.Dinners.SingleOrDefault(d => d.DinnerID == dinner.DinnerId);

            foreach (RSVP rsvp in entity.RSVPs.ToList())
                db.RSVPs.DeleteObject(rsvp);
            db.Dinners.DeleteObject(entity);
        }

       


        public IList<DinnerViewInfo> FindByLocation(float latitude, float longitude)
        {
            IEnumerable<Dinner> asEnumerable = NearestDinners(latitude, longitude).AsEnumerable();
            IQueryable<Dinner> dinners = from dinner in
                                             (from dinner in db.Dinners
                                              where dinner.EventDate >= DateTime.Now
                                              orderby dinner.EventDate
                                              select dinner)
                                         join i in asEnumerable
                                             on dinner.DinnerID equals i.DinnerID
                                         select dinner;

            return DinnerViewInfoMapper.CreateFrom(dinners);
        }



        [EdmFunction("NerdDinnerModel.Store", "DistanceBetween")]
        public static double DistanceBetween(double lat1, double long1, double lat2, double long2)
        {
            throw new NotImplementedException("Only call through LINQ expression");
        }

        private IQueryable<Dinner> NearestDinners(double latitude, double longitude)
        {
            return from d in db.Dinners
                   where DistanceBetween(latitude, longitude, d.Latitude, d.Longitude) < 100
                   select d;
        }


        public void Delete(int dinnerId)
        {
            var dinner =  db.Dinners.SingleOrDefault(d => d.DinnerID == dinnerId);
            if (dinner == null)
            {
                throw new ApplicationException(string.Format("dinner not found with tech id : {0}", dinnerId));
            }
            else
            {
                foreach (RSVP rsvp in dinner.RSVPs.ToList())
                {
                    db.RSVPs.DeleteObject(rsvp);
                }
                db.Dinners.DeleteObject(dinner);
                db.SaveChanges();
            }
        }


        public void RegisterForDinner(int dinnerId, string userName)
        {
            var dinner = db.Dinners.SingleOrDefault(d => d.DinnerID == dinnerId);
            if (dinner == null)
            {
                throw new ApplicationException(string.Format("dinner not found with tech id : {0}", dinnerId));
            }
            if (!dinner.IsUserRegistered(userName))
            {
                var rsvp = new RSVP();
                rsvp.AttendeeName = userName;
                dinner.RSVPs.Add(rsvp);
                db.SaveChanges();
            }
        }


        public int CreateDinner(DinnerInfo dinner)
        {

            Dinner entity = Mapper.CreateFrom(dinner);

           

            var rsvp = new RSVP();
            rsvp.AttendeeName = dinner.HostedBy;
            entity.RSVPs.Add(rsvp);

            db.Dinners.AddObject(entity);



            db.SaveChanges();
            return entity.DinnerID;
        }


        public void UpdateDinner(DinnerInfo dinner)
        {
            var dinnerToUpdated = db.Dinners.SingleOrDefault(d => d.DinnerID == dinner.DinnerId);
            dinnerToUpdated.Address = dinner.Address;
            dinnerToUpdated.ContactPhone = dinner.ContactPhone;
            dinnerToUpdated.Country = dinner.Country;
            dinnerToUpdated.Description = dinner.Description;
            dinnerToUpdated.EventDate = dinner.EventDate;
            dinnerToUpdated.Latitude = dinner.Latitude;
            dinnerToUpdated.Longitude = dinner.Longitude;
            db.SaveChanges();
        }
    }
}